﻿/****************************** Module Header ******************************\
Module Name:    ParsingMetrics.cs
Project:        SQL Server Error Log Parsing module

This class contains run-time information about the parsing of a single 
SQL Server error log.  These metrics can be used to evaluate the efficiency 
of the Get-ErrorLogEvent Cmdlet, troubleshoot problems with the 
Get-ErrorLogEvent Cmdlet, and even improve the Cmdlet's performance by 
providing the historical popularity of system message types.

This source is subject to the Microsoft Public License.
See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
All other rights reserved.

THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
\***************************************************************************/

using System;
using System.Collections.Generic;
using System.Text;

namespace SQLServerSleuth.PowerShell.Commands.ParseERRORLOG
{
    /// <summary>
    /// This class contains run-time information about the parsing of a 
    /// single SQL Server error log.
    /// </summary>
    public class ParsingMetrics
    {
        /// <summary>
        /// The solitary constructor for the class assigns the given values 
        /// to many the private fields.
        /// </summary>
        /// <param name="ErrorLogPath">The path to SQL Server error log file 
        /// that was parsed.</param>
        /// <param name="MessageTemplateFilePath">The path to the file that 
        /// contains the system message templates that were used to parse 
        /// the SQL Server error log.</param>
        /// <param name="MessageTemplateCount">The number of templates in 
        /// the message template file.</param>
        /// <param name="HighestTokenCount">The highest number of tokens 
        /// found in a single message template that was loaded from the 
        /// message template file.</param>
        /// <param name="SourceLineCount">The number of lines in the source 
        /// SQL Server error log file.</param>
        /// <param name="StartTime">The time at which the parsing began.</param>
        /// <param name="FinishTime">The time at which the parsing completed.</param>
        /// <param name="MessageMatchCounts">A collection of statistics on 
        /// the number of times that each message type was parsed, both from 
        /// current SQL Server error log file as well as historically.</param>
        /// <param name="EarliestEntryTime">The date/time of the earliest 
        /// entry parsed from the SQL Server error log.</param>
        /// <param name="LatestEntryTime">The date/time of the latest entry 
        /// parsed from the SQL Server error log.</param>
        public ParsingMetrics
        (
            string ErrorLogPath,
            string MessageTemplateFilePath,
            int MessageTemplateCount,
            int HighestTokenCount,
            int SourceLineCount,
            DateTime StartTime,
            DateTime FinishTime,
            MessageMatchCounts MessageMatchCounts,
            DateTime EarliestEntryTime,
            DateTime LatestEntryTime
        )
        {
            this._earliestEntryTime = EarliestEntryTime;
            this._errorLogPath = ErrorLogPath;
            this._finishTime = FinishTime;
            this._latestEntryTime = LatestEntryTime;
            this._highestTokenCount = HighestTokenCount;
            this._messageMatchCounts = MessageMatchCounts;
            this._messageTemplateCount = MessageTemplateCount;
            this._messageTemplateFilePath = MessageTemplateFilePath;
            this._sourceLineCount = SourceLineCount;
            this._startTime = StartTime;

            return;
        }

        /// <summary>
        /// A format pattern to use when converting DataTime values to 
        /// strings in the ToXML() method.  This is the only settable 
        /// property of the class.
        /// </summary>
        public string DateTimeStringFormat
        {
            get
            {
                return this._dateTimeStringFormat;
            }

            set
            {
                this._dateTimeStringFormat = value.Trim();
            }
        }
        private string _dateTimeStringFormat = "yyyy-MM-ddTHH:mm:ss.fffzzz";

        /// <summary>
        /// See the corresponding parameter of the <see cref="ParsingMetrics"/> constructor.
        /// </summary>
        public DateTime EarliestEntryTime
        {
            get
            {
                return this._earliestEntryTime;
            }
        }
        private DateTime _earliestEntryTime;

        /// <summary>
        /// See the corresponding parameter of the <see cref="ParsingMetrics"/> constructor.
        /// </summary>
        public string ErrorLogPath
        {
            get
            {
                return this._errorLogPath;
            }
        }
        private string _errorLogPath;

        /// <summary>
        /// See the corresponding parameter of the <see cref="ParsingMetrics"/> constructor.
        /// </summary>
        public DateTime FinishTime
        {
            get
            {
                return this._finishTime;
            }
        }
        private DateTime _finishTime;

        /// <summary>
        /// See the corresponding parameter of the <see cref="ParsingMetrics"/> constructor.
        /// </summary>
        public int HighestTokenCount
        {
            get
            {
                return this._highestTokenCount;
            }
        }
        private int _highestTokenCount;

        /// <summary>
        /// See the corresponding parameter of the <see cref="ParsingMetrics"/> constructor.
        /// </summary>
        public DateTime LatestEntryTime
        {
            get
            {
                return this._latestEntryTime;
            }
        }
        private DateTime _latestEntryTime;

        /// <summary>
        /// See the corresponding parameter of the <see cref="ParsingMetrics"/> constructor.
        /// </summary>
        public MessageMatchCounts MessageMatchCounts
        {
            get
            {
                return this._messageMatchCounts;
            }
        }
        private MessageMatchCounts _messageMatchCounts;

        /// <summary>
        /// See the corresponding parameter of the <see cref="ParsingMetrics"/> constructor.
        /// </summary>
        public int MessageTemplateCount
        {
            get
            {
                return this._messageTemplateCount;
            }
        }
        private int _messageTemplateCount;

        /// <summary>
        /// See the corresponding parameter of the <see cref="ParsingMetrics"/> constructor.
        /// </summary>
        public string MessageTemplateFilePath
        {
            get
            {
                return this._messageTemplateFilePath;
            }
        }
        private string _messageTemplateFilePath;

        /// <summary>
        /// See the corresponding parameter of the <see cref="ParsingMetrics"/> constructor.
        /// </summary>
        public int SourceLineCount
        {
            get
            {
                return this._sourceLineCount;
            }
        }
        private int _sourceLineCount;

        /// <summary>
        /// See the corresponding parameter of the <see cref="ParsingMetrics"/> constructor.
        /// </summary>
        public DateTime StartTime
        {
            get
            {
                return this._startTime;
            }
        }
        private DateTime _startTime;

        /// <summary>
        /// This method creates an XML representation of the ParsingMetrics 
        /// object.
        /// </summary>
        /// <returns>An XML docuent that represents the ParsingMetrics 
        /// object.</returns>
        public System.Xml.XmlDocument ToXML()
        {
            //  For ease of implementation, the XML is generated by string 
            //  concatenation instead of using the XML object model.  For 
            //  the sake of readability, the string is created in several 
            //  steps (instead of all-at-once).  Because of this, a 
            //  StringBuilder object was chosen to facilitate efficient 
            //  string concatenation.
            StringBuilder parsingMetricsXMLContents = new StringBuilder();

            //  Create the XML prolog, the root node, and a node for the 
            //  name of the machine on which the error log parsing was 
            //  performed.
            parsingMetricsXMLContents.AppendFormat
            (
                "<?xml version=\"1.0\" standalone=\"yes\"?><parsingMetrics><hostName><![CDATA[{0}]]></hostName>",
                System.Environment.MachineName
            );

            //  Add details of the time that parsing operation consumed and 
            //  the process ID of the current PowerShell host.
            parsingMetricsXMLContents.AppendFormat
            (
                "<execution><startTime>{0}</startTime><finishTime>{1}</finishTime><duration units=\"milliseconds\">{2}</duration><processID>{3}</processID></execution>",
                this._startTime.ToString(this._dateTimeStringFormat),
                this._finishTime.ToString(this._dateTimeStringFormat),
                (this._finishTime - this._startTime).TotalMilliseconds,
                System.Diagnostics.Process.GetCurrentProcess().Id
            );

            //  Add details about the system message templates that were 
            //  used to parse the SQL Server error log.
            parsingMetricsXMLContents.AppendFormat
            (
                "<messageTemplates><filePath><![CDATA[{0}]]></filePath><templateCount>{1}</templateCount><highestTokenCount>{2}</highestTokenCount></messageTemplates>",
                this._messageTemplateFilePath,
                this._messageTemplateCount,
                this._highestTokenCount
            );

            //  Add details about the SQL Server error log that was parsed.
            parsingMetricsXMLContents.AppendFormat
            (
                "<errorLog><filePath><![CDATA[{0}]]></filePath><lineCount>{1}</lineCount><earliestEntryTime>{2}</earliestEntryTime><latestEntryTime>{3}</latestEntryTime></errorLog>",
                this._errorLogPath,
                this._sourceLineCount,
                this._earliestEntryTime.ToString(this._dateTimeStringFormat),
                this._latestEntryTime.ToString(this._dateTimeStringFormat)
            );

            //  Use the default Comparer to sort the collection of 
            //  MessageMatchCount objects in descending order of matches 
            //  (and ascending order of message IDs if there are match count 
            //  ties).
            this._messageMatchCounts.Sort();

            //  Iterate through the MessageMatchCounts collection and add 
            //  each to the output.
            parsingMetricsXMLContents.Append("<messageStatistics>");
            foreach (MessageMatchCount messageMatchCount in this._messageMatchCounts)
            {
                parsingMetricsXMLContents.AppendFormat
                (
                    "<message id=\"{0}\"><matchCount type=\"current\">{1}</matchCount><matchCount type=\"historical\">{2}</matchCount></message>",
                    messageMatchCount.MessageID,
                    messageMatchCount.CurrentLogFileMatchCount,
                    messageMatchCount.HistoricalMatchCount
                );
            }
            parsingMetricsXMLContents.Append("</messageStatistics>");

            //  Finalize the XML document by closing the root node.
            parsingMetricsXMLContents.Append("</parsingMetrics>");

            //  Convert the string into the XML document that will be 
            //  returned to the calling context.
            System.Xml.XmlDocument parsingMetricsXMLDocument = new System.Xml.XmlDocument();
            parsingMetricsXMLDocument.LoadXml(parsingMetricsXMLContents.ToString());

            return parsingMetricsXMLDocument;
        }
    }
}
